﻿using System;
using System.Collections.Generic;
using System.Text;
using AIStudio.Wpf.DiagramDesigner.Geometrys;

namespace AIStudio.Wpf.DiagramDesigner
{
    public static partial class PathGenerators
    {
        public static PathGeneratorResult Boundary(IDiagramViewModel _, ConnectionViewModel link, PointBase[] route, PointBase source, PointBase target)
        {
            route = ConcatRouteAndSourceAndTarget(route, source, target);

            if (route.Length > 2)
                return CurveThroughPoints(route, link);

            route = GetRouteWithMiddlePoints(_, link, route);

            double sourceAngle = SourceMarkerAdjustement(route, link.GetSourceMarkerWidth(), link.GetSourceMarkerHeight());
            double targetAngle = TargetMarkerAdjustement(route, link.GetSinkMarkerWidth(), link.GetSinkMarkerHeight());

            DoShift(route, link);

            var paths = new string[route.Length - 1];
            for (var i = 0; i < route.Length - 1; i++)
            {
                paths[i] = FormattableString.Invariant($"M {route[i].X} {route[i].Y} L {route[i + 1].X} {route[i + 1].Y}");
            }

            return new PathGeneratorResult(paths, sourceAngle, route[0], targetAngle, route[route.Length - 1]);
        }

        private static PointBase[] GetRouteWithMiddlePoints(IDiagramViewModel _, ConnectionViewModel link, PointBase[] route)
        {
            var middle = GetMiddlePoints(
                link.SourceConnectorInfo.MiddlePosition,
                link.SourceConnectorInfo.Orientation,
                link.SinkConnectorInfo.MiddlePosition,
                link.IsFullConnection ? link.SinkConnectorInfoFully.Orientation : (link.SinkConnectorInfo.MiddlePosition.Y >= link.SourceConnectorInfo.MiddlePosition.Y ? ConnectorOrientation.Top : ConnectorOrientation.Bottom),
                _.DiagramOption.LayoutOption.GridCellSize,
                _.DiagramOption.LayoutOption.GridMarginSize);

            middle.Insert(0, route[0]);
            middle.Add(route[1]);

            return middle.ToArray();
        }

        private static List<PointBase> GetMiddlePoints(PointBase source, ConnectorOrientation sourceOrientation, PointBase sink, ConnectorOrientation sinkOrientation, SizeBase gridCellSize, SizeBase gridMargin)
        {
            var points = new List<PointBase>();

            var p0 = GetFirstSegment(source, sourceOrientation, gridCellSize, gridMargin);
            var p1 = GetFirstSegment(sink, sinkOrientation, gridCellSize, gridMargin);

            if (p0.Equals(p1))
                return points;

            var p2 = new PointBase(GetNearestCross(p0.X, p1.X, gridCellSize.Width, gridMargin.Width), GetNearestCross(p0.Y, p1.Y, gridCellSize.Height, gridMargin.Height));
            var p3 = new PointBase(GetNearestCross(p1.X, p0.X, gridCellSize.Width, gridMargin.Width), GetNearestCross(p1.Y, p0.Y, gridCellSize.Height, gridMargin.Height));
            if (p2.Equals(p3))
            {
                points.Add(p0);
                points.Add(p2);
                points.Add(p1);
            }
            else
            {
                points.Add(p0);
                points.Add(p2);
                if (!(Math.Abs(p2.X - p3.X) < 0.0001) && !(Math.Abs(p2.Y - p3.Y) < 0.0001))
                    points.Add(new PointBase(p2.X, p3.Y));
                points.Add(p3);
                points.Add(p1);
            }

            return points;
        }

        private static PointBase GetFirstSegment(PointBase point, ConnectorOrientation orientation, SizeBase cellSize, SizeBase margin)
        {
            double x = ((int)((point.X - margin.Width) / cellSize.Width) + 0.5) * cellSize.Width + margin.Width;
            double y = ((int)((point.Y - margin.Height) / cellSize.Height) + 0.5) * cellSize.Height + margin.Height;
            if (orientation == ConnectorOrientation.Top)
                return new PointBase(x, y - 0.5 * cellSize.Height);
            else if (orientation == ConnectorOrientation.Bottom)
                return new PointBase(x, y + 0.5 * cellSize.Height);
            else if (orientation == ConnectorOrientation.Left)
                return new PointBase(x - 0.5 * cellSize.Width, y);
            else
                return new PointBase(x + 0.5 * cellSize.Width, y);
        }

        public static double GetNearestCross(double a, double b, double cellSize, double margin)
        {
            if (Math.Abs(a - b) < 0.0001 && (int)(Math.Round((a - margin)/ cellSize, 3)) == (Math.Round((a - margin) / cellSize,3)))
                return a;
            else if (a < b)
                return Math.Ceiling(Math.Round((a - margin) / cellSize, 3)) * cellSize + margin;
            else
                return Math.Floor(Math.Round((a - margin) / cellSize, 3)) * cellSize + margin;
        }

        public static PointBase SegmentMiddlePoint(PointBase p1, PointBase p2)
        {
            return new PointBase((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2);
        }
    }
}
